
# step 11 Run non-linear dimensional reduction (UMAP/tSNE)
# If you haven't installed UMAP, you can do so via reticulate::py_install(packages = 'umap-learn')
pbmc <- RunUMAP(pbmc, dims = 1:10)

# note that you can set `label = TRUE` or use the LabelClusters function to help label
# individual clusters
DimPlot(pbmc, reduction = "umap")

# saveRDS(pbmc, file = "../output/pbmc_tutorial.rds")


$ find .. | grep "R$" | xargs grep -in "DimPlot" 2>/dev/null --color=auto
../R_Seurat_reading/seurat-4.1.0/R/generics.R:532:#' DimPlot(object = pbmc_small, reduction = 'umap')
../R_Seurat_reading/seurat-4.1.0/R/visualization.R:787:DimPlot <- function(




(1) DimPlot()

#' Dimensional reduction plot
#'
#' Graphs the output of a dimensional reduction technique on a 2D scatter plot where each point is a
#' cell and it's positioned based on the cell embeddings determined by the reduction technique. By
#' default, cells are colored by their identity class (can be changed with the group.by parameter).
#'
#' @param object Seurat object
#' @param dims Dimensions to plot, must be a two-length numeric vector specifying x- and y-dimensions
长度为2的向量，指定x和y轴。

#' @param cells Vector of cells to plot (default is all cells)
#' @param cols Vector of colors, each color corresponds to an identity class. This may also be a single character
#' or numeric value corresponding to a palette as specified by \code{\link[RColorBrewer]{brewer.pal.info}}.
#' By default, ggplot2 assigns colors. We also include a number of palettes from the pals package.
#' See \code{\link{DiscretePalette}} for details.
颜色向量，对应着 ident 分类。 
也可以是一个字符串或数字，对应着 RColorBrewer::brewer.pal.info
默认使用 ggplot2 的颜色。
本包也提供了一些颜色板，DiscretePalette 回车查看


#' @param pt.size Adjust point size for plotting 点的大小。

#' @param reduction Which dimensionality reduction to use. If not specified, first searches for umap, then tsne, then pca
使用哪个降维。如果未指定，则依次找 umap, tsne, pca.

#' @param group.by Name of one or more metadata columns to group (color) cells by
#' (for example, orig.ident); pass 'ident' to group by identity class
分组方式，可以输入一个 metadata 的列，来给细胞上色。默认是 ident。

#' @param split.by Name of a metadata column to split plot by;
#' see \code{\link{FetchData}} for more details
是否分面。可以给一个 metadata 列名。 参考 FetchData

#' @param shape.by If NULL, all points are circles (default). You can specify any
#' cell attribute (that can be pulled with FetchData) allowing for both
#' different colors and different shapes on cells.  Only applicable if \code{raster = FALSE}.
形状。如果null，则全部是圆圈。参考 FetchData 可以拿到的值。
可以指定细胞同时使用不同的形状和颜色。仅在 raster=F时有效。

#' @param order Specify the order of plotting for the idents. This can be
#' useful for crowded plots if points of interest are being buried. Provide
#' either a full list of valid idents or a subset to be plotted last (on top)
指定画点的顺序，适用于互相遮盖的情况。 可以提供一个 idents list 或者 最后化的点的子集(画到顶部)。

#' @param shuffle Whether to randomly shuffle the order of points. This can be
#' useful for crowded plots if points of interest are being buried. (default is FALSE)
是否洗牌点的顺序，适用于互相遮挡的情况。默认F。

#' @param seed Sets the seed if randomly shuffling the order of points.
洗牌随机数种子。

#' @param label Whether to label the clusters
类是否标注文字。默认F。

#' @param label.size Sets size of labels
文字标签的大小。

#' @param label.color Sets the color of the label text
文字标签的颜色。

#' @param label.box Whether to put a box around the label text (geom_text vs
#' geom_label)
文字标签是否加box: geom_text vs geom_label.


#' @param repel Repel labels 文字标签是否防遮挡。

#' @param cells.highlight A list of character or numeric vectors of cells to
#' highlight. If only one group of cells desired, can simply
#' pass a vector instead of a list. If set, colors selected cells to the color(s)
#' in \code{cols.highlight} and other cells black (white if dark.theme = TRUE);
#' will also resize to the size(s) passed to \code{sizes.highlight}
高亮显示的细胞，list格式。
如果仅高亮显示一个组，可以传一个vector。

#' @param cols.highlight A vector of colors to highlight the cells as; will
#' repeat to the length groups in cells.highlight
#' @param sizes.highlight Size of highlighted cells; will repeat to the length
#' groups in cells.highlight
高亮显示的细胞的颜色、大小。


#' @param na.value Color value for NA points when using custom scale
自定义坐标时，NA点的颜色。

#' @param ncol Number of columns for display when combining plots
合并图像时几列?

#' @param combine Combine plots into a single \code{\link[patchwork]{patchwork}ed}
#' ggplot object. If \code{FALSE}, return a list of ggplot objects
是否合并图像？ 如果F，则返回 ggplot obj list.

#' @param raster Convert points to raster format, default is \code{NULL} which
#' automatically rasterizes if plotting more than 100,000 cells
是否栅格化(图像中的点是拼合后的位图，不再是矢量图)。如果点超过10万个，自动栅格化。

#' @param raster.dpi Pixel resolution for rasterized plots, passed to geom_scattermore().
#' Default is c(512, 512).
栅格化后的分辨率，传递给 geom_scattermore()，默认 c(512, 512)

#'
#' @return A \code{\link[patchwork]{patchwork}ed} ggplot object if
#' \code{combine = TRUE}; otherwise, a list of ggplot objects
T则返回一个 ggplot 对象。F则返回一个ggplot obj list.

#'
#' @importFrom rlang !!
#' @importFrom ggplot2 facet_wrap vars sym labs
#' @importFrom patchwork wrap_plots
#'
#' @export
#' @concept visualization
#'
#' @note For the old \code{do.hover} and \code{do.identify} functionality, please see
#' \code{HoverLocator} and \code{CellSelector}, respectively.
#'
#' @aliases TSNEPlot PCAPlot ICAPlot
#' @seealso \code{\link{FeaturePlot}} \code{\link{HoverLocator}}
#' \code{\link{CellSelector}} \code{\link{FetchData}}
#'
#' @examples
#' data("pbmc_small")
#' DimPlot(object = pbmc_small)
#' DimPlot(object = pbmc_small, split.by = 'ident')
#'
DimPlot <- function(
  object,
  dims = c(1, 2),
  cells = NULL,
  cols = NULL,
  pt.size = NULL,
  reduction = NULL,
  group.by = NULL,
  split.by = NULL,
  shape.by = NULL,
  order = NULL,
  shuffle = FALSE,
  seed = 1,

  label = FALSE,
  label.size = 4,
  label.color = 'black',
  label.box = FALSE,

  repel = FALSE,

  cells.highlight = NULL,
  cols.highlight = '#DE2D26',
  sizes.highlight = 1,

  na.value = 'grey50',

  ncol = NULL,
  combine = TRUE,
  raster = NULL,
  raster.dpi = c(512, 512)
) {
  # (A1) 如果没提供2个坐标编号，则报错。
  if (length(x = dims) != 2) {
    stop("'dims' must be a two-length vector")
  }

  # (A2) 降维数据，默认使用 umap
  reduction <- reduction %||% DefaultDimReduc(object = object) #DefaultDimReduc(object = pbmc) [1] "umap"
  #(A3) 细胞，默认使用全部
  cells <- cells %||% colnames(x = object)
  #(A4) 获取嵌入坐标
  #head(Embeddings(pbmc[['umap']]))
  #                    UMAP_1     UMAP_2
  #AAACATACAACCAC-1  2.864640   4.076900
  #AAACATTGAGCTAC-1  5.019568 -12.472298
  data <- Embeddings(object = object[[reduction]])[cells, dims]

  #(A5) 转为数据框
  data <- as.data.frame(x = data)
  #(A6) 获取坐标 列名，作为坐标轴名字
  # > Key(pbmc[['umap']]) #[1] "UMAP_"
  dims <- paste0(Key(object = object[[reduction]]), dims)

  # (A7)在 metadata 中 生成一列 ident，只在函数内有效。
  object[['ident']] <- Idents(object = object)
  

  #(A8) 获取分组
  orig.groups <- group.by #备份分组列名
  group.by <- group.by %||% 'ident' #如果为空，则使用 ident 列
  #(A9) 给点坐标df添加新列: 分组，这个分组可能是多个分组
  data <- cbind(data, object[[group.by]][cells, , drop = FALSE])
  #(A10) 获取刚合成的数据的列名，只要第三个及之后的。
  group.by <- colnames(x = data)[3:ncol(x = data)]
  #(A11) 把分组列都强转为因子
  for (group in group.by) {
    if (!is.factor(x = data[, group])) {
      data[, group] <- factor(x = data[, group])
    }
  }

  #(A12) 形状列非空，则添加一列
  if (!is.null(x = shape.by)) {
    data[, shape.by] <- object[[shape.by, drop = TRUE]]
  }

  #(A13) 分面列非空，则添加一列
  if (!is.null(x = split.by)) {
  	# 如果这里 split.by 找不到，就已经报错了，不可能走到 LabelClusters() 的 A3 部分。
    data[, split.by] <- object[[split.by, drop = TRUE]]
  }

  #(A14) 如果需要打乱细胞顺序
  if (isTRUE(x = shuffle)) {
    set.seed(seed = seed)
    # 则按照随机数种子，打乱每行的顺序。
    data <- data[sample(x = 1:nrow(x = data)), ]
  }


  #(A15) 开始画图
  plots <- lapply(
    X = group.by, #遍历分组，如果没有，默认是 ident 列
    FUN = function(x) {
      # (B1)主图
      plot <- SingleDimPlot(
        data = data[, c(dims, x, split.by, shape.by)],
        dims = dims,
        col.by = x,
        cols = cols,
        pt.size = pt.size,
        shape.by = shape.by,
        order = order,
        label = FALSE,
        cells.highlight = cells.highlight,
        cols.highlight = cols.highlight,
        sizes.highlight = sizes.highlight,
        na.value = na.value,
        raster = raster,
        raster.dpi = raster.dpi
      )

      #(B2)如果标记，则添加标签文字
      if (label) {
        plot <- LabelClusters(
          plot = plot,
          id = x, #x 是 group.by 的每个 uniq 值
          repel = repel,
          size = label.size,
          split.by = split.by,
          box = label.box,
          color = label.color
        )
      }
      #(B3) 如果分面(默认不走这里)
      if (!is.null(x = split.by)) {
        plot <- plot + FacetTheme() +
          facet_wrap(
            facets = vars(!!sym(x = split.by)), # todo?? 怎么理解？ 见本文 3.2
            # 如果分组大于1， 或者 列数 为空
            ncol = if (length(x = group.by) > 1 || is.null(x = ncol)) {
              # 则列数为: split.by 默认 NULL，
              length(x = unique(x = data[, split.by]))
            } else {
              ncol
            }
          )
      }

      #(B4) 如果原始分组为空(默认)
      plot <- if (is.null(x = orig.groups)) {
      	# 标题为空
        plot + labs(title = NULL)
      } else {
      	# 有分组的，标题居中。
        plot + CenterTitle()
      }
    }
  )


  #(A16) 如果分面非空，则ncol=1
  if (!is.null(x = split.by)) {
    ncol <- 1 #有split.by 则 ncol=1 ??
  }


  #(A17) 默认T
  if (combine) {
  	# 
    plots <- wrap_plots(plots, ncol = orig.groups %iff% ncol) #感觉这个写的有问题，怪怪的？小毛病，跳过吧。
  }

  return(plots)
}














()SingleDimPlot
$ find .. | grep "R$" | xargs grep -in "SingleDimPlot" 2>/dev/null --color=auto
../R_Seurat_reading/seurat-4.1.0/R/visualization.R:7033:SingleDimPlot <- function(


#' Plot a single dimension
#'
#' @param data Data to plot
#' @param dims A two-length numeric vector with dimensions to use
#' @param col.by ...
#' @param cols Vector of colors, each color corresponds to an identity class.
#' This may also be a single character or numeric value corresponding to a
#' palette as specified by \code{\link[RColorBrewer]{brewer.pal.info}}.By
#' default, ggplot2 assigns colors
#' @param pt.size Adjust point size for plotting
#' @param shape.by If NULL, all points are circles (default). You can specify
#' any cell attribute (that can be pulled with \code{\link{FetchData}})
#' allowing for both different colors and different shapes on cells.
#' @param alpha.by Mapping variable for the point alpha value
#' @param order Specify the order of plotting for the idents. This can be
#' useful for crowded plots if points of interest are being buried. Provide
#' either a full list of valid idents or a subset to be plotted last (on top).
#' @param label Whether to label the clusters
#' @param repel Repel labels
#' @param label.size Sets size of labels
#' @param cells.highlight A list of character or numeric vectors of cells to
#' highlight. If only one group of cells desired, can simply
#' pass a vector instead of a list. If set, colors selected cells to the color(s)
#' in \code{cols.highlight} and other cells black (white if dark.theme = TRUE);
#' will also resize to the size(s) passed to \code{sizes.highlight}
#' @param cols.highlight A vector of colors to highlight the cells as; will
#' repeat to the length groups in cells.highlight
#' @param sizes.highlight Size of highlighted cells; will repeat to the length
#' groups in cells.highlight
#' @param na.value Color value for NA points when using custom scale.
#' @param raster Convert points to raster format, default is \code{NULL}
#' which will automatically use raster if the number of points plotted is
#' greater than 100,000
#' @param raster.dpi the pixel resolution for rastered plots, passed to geom_scattermore().
#' Default is c(512, 512)
#'
#' @return A ggplot2 object
#'
#' @importFrom cowplot theme_cowplot
#' @importFrom RColorBrewer brewer.pal.info
#' @importFrom ggplot2 ggplot aes_string geom_point labs guides scale_color_brewer
#' scale_color_manual element_rect guide_legend discrete_scale
#'
#' @keywords internal
#'
#' @export
#'
SingleDimPlot <- function(
  data,
  dims,
  col.by = NULL,
  cols = NULL,
  pt.size = NULL,
  shape.by = NULL,
  alpha.by = NULL,
  order = NULL,
  label = FALSE,
  repel = FALSE,
  label.size = 4,
  cells.highlight = NULL,
  cols.highlight = '#DE2D26',
  sizes.highlight = 1,
  na.value = 'grey50',
  raster = NULL,
  raster.dpi = NULL
) {
  #(A1) 点的大小，默认 AutoPointSize()函数，见 解析 12-2.3
  pt.size <- pt.size %||% AutoPointSize(data = data, raster = raster)

  #(A2) 如果超过10万个点，且raster不是F，提醒：栅格化了。
  if ((nrow(x = data) > 1e5) & !isFALSE(raster)){
    message("Rasterizing points since number of points exceeds 100,000.",
            "\nTo disable this behavior set `raster=FALSE`")
  }
  raster <- raster %||% (nrow(x = data) > 1e5)

  #(A3) 栅格化分辨率，如果非空
  if (!is.null(x = raster.dpi)) {
  	# 不是数字，或者长度不是2，报错
    if (!is.numeric(x = raster.dpi) || length(x = raster.dpi) != 2)
      stop("'raster.dpi' must be a two-length numeric vector")
  }
  
  #(A4) 维度必须是2，否则报错
  if (length(x = dims) != 2) {
    stop("'dims' must be a two-length vector")
  }

  #(A5) 强转为数据框
  if (!is.data.frame(x = data)) {
    data <- as.data.frame(x = data)
  }

  #(A6) dims是字符串，且  dims 不全在 data 的列名中，报错
  if (is.character(x = dims) && !all(dims %in% colnames(x = data))) {
    stop("Cannot find dimensions to plot in data")
  } else if (is.numeric(x = dims)) { #否则，如果是数字
  	# 则 dims 由数字获取字符串
    dims <- colnames(x = data)[dims]
  }

  #(A7) 高亮细胞 非空
  if (!is.null(x = cells.highlight)) {
  	# 则设置高亮，返回一个4元素list： plot.order/highlight/size/color
    highlight.info <- SetHighlight(
      cells.highlight = cells.highlight,
      cells.all = rownames(x = data),
      sizes.highlight = sizes.highlight %||% pt.size,
      cols.highlight = cols.highlight,
      col.base = cols[1] %||% '#C3C3C3',
      pt.size = pt.size
    )

    order <- highlight.info$plot.order #2个字符串
    data$highlight <- highlight.info$highlight #两个因子的向量
    col.by <- 'highlight'
    pt.size <- highlight.info$size #点的大小和颜色
    cols <- highlight.info$color
  }



  #(A8) 如果order非空，且 col.by 非空。 
  # 如果走了 A7, 则必然走 A8。
  # 如果没有走 A7，则 cells.highlight 为空。 
  if (!is.null(x = order) && !is.null(x = col.by)) {
  	#(B1) 如果 order 是逻辑型变量
    if (typeof(x = order) == "logical") {
      if (order) {
      	# 且 order =T时，按照两列排序: col.by列非na，col.by 列升序排序
        data <- data[order(!is.na(x = data[, col.by]), data[, col.by]), ]
      }
    #(B2) 否则，如果不是逻辑值
    } else {
      # 则按照字符串反序
      order <- rev(x = c(
        order, # 2个字符串，
        setdiff(x = unique(x = data[, col.by]), y = order) #取差集: 正常这应该是空集。啥时候非空呢？ //todo
      ))
      
      # 高亮显示列强转为因子
      data[, col.by] <- factor(x = data[, col.by], levels = order)
      # 排序，可合为一行。但下一行也用这个序号，所以分开写了。
      new.order <- order(x = data[, col.by])
      data <- data[new.order, ]
      # 如果点大小 和 数据等长，则对点的大小值同样排序
      if (length(x = pt.size) == length(x = new.order)) {
        pt.size <- pt.size[new.order]
      }
    }
  }


  #(A9) 如果 col.by 非空，且 col.by 不在 data 的列名中，则警告，并设置 col.by 为空
  if (!is.null(x = col.by) && !col.by %in% colnames(x = data)) {
    warning("Cannot find ", col.by, " in plotting data, not coloring plot")
    col.by <- NULL
  # 否则
  } else {
  	# 获取 col.by 在 data 列名中的序号
    # col.index <- grep(pattern = col.by, x = colnames(x = data), fixed = TRUE)
    col.index <- match(x = col.by, table = colnames(x = data))
    # 如果匹配到数字
    if (grepl(pattern = '^\\d', x = col.by)) {
      # Do something for numbers
      col.by <- paste0('x', col.by) #加x前缀
    # 否则，如果匹配到-号
    } else if (grepl(pattern = '-', x = col.by)) {
      # Do something for dashes
      # 则替换-为.
      col.by <- gsub(pattern = '-', replacement = '.', x = col.by)
    }
    # 列名更新
    colnames(x = data)[col.index] <- col.by
  }


  #(A10) 如果 shape.by 非空，且不在 data 列名中
  if (!is.null(x = shape.by) && !shape.by %in% colnames(x = data)) {
  	# 警告找不到
    warning("Cannot find ", shape.by, " in plotting data, not shaping plot")
  }

  #(A11) 如果 alpha.by 非空，且 alpha.by 不在 data 列名中
  if (!is.null(x = alpha.by) && !alpha.by %in% colnames(x = data)) {
  	# 警告找不到，alpha.by 设为空
    warning(
      "Cannot find alpha variable ",
      alpha.by,
      " in data, setting to NULL",
      call. = FALSE,
      immediate. = TRUE
    )
    alpha.by <- NULL
  }

  ###################
  # 开始 ggplot2 画图
  ###################
  #(A12) 画图，指定数据
  plot <- ggplot(data = data)

  #(A13) 散点: 如果要栅格化，超过10万点自动栅格化
  plot <- if (isTRUE(x = raster)) {
  	# 进行栅格化绘图
    plot + geom_scattermore(
      mapping = aes_string(
        x = dims[1],
        y = dims[2],
        color = paste0("`", col.by, "`"),
        shape = shape.by,
        alpha = alpha.by
      ),
      pointsize = pt.size,
      pixels = raster.dpi
    )
  # 否则不栅格化(<10万点，默认走这里)
  } else {
    plot + geom_point(
      mapping = aes_string(
        x = dims[1],
        y = dims[2],
        color = paste0("`", col.by, "`"), #颜色写法？
        shape = shape.by,
        alpha = alpha.by
      ),
      size = pt.size
    )
  }


  #(A14) 放大图例的点
  plot <- plot +
    guides(color = guide_legend(override.aes = list(size = 3))) +
    labs(color = NULL, title = col.by) + #图例标题为空
    CenterTitle()


  #(A15) 要标记文字，且 col.by 非空
  if (label && !is.null(x = col.by)) {
  	# 则添加文字标记
    plot <- LabelClusters(
      plot = plot,
      id = col.by,
      repel = repel,
      size = label.size
    )
  }


  #(A16) 颜色非空
  if (!is.null(x = cols)) {
  	# 如果长度为1，且是数字。 或者 cols 在 色板名字中
    if (length(x = cols) == 1 && (is.numeric(x = cols) || cols %in% rownames(x = brewer.pal.info))) {
      # 如果是数字，这里没考虑 type 参数（默认 'seq'），算小 bug 吧
      scale <- scale_color_brewer(palette = cols, na.value = na.value)
    # 否则，如果长度为1，且在以下几个里（这几个是 Seurat 内置的5个颜色方案）
    } else if (length(x = cols) == 1 && (cols %in% c('alphabet', 'alphabet2', 'glasbey', 'polychrome', 'stepped'))) {
      # 获取颜色字符串数组
      colors <- DiscretePalette(length(unique(data[[col.by]])), palette = cols)
      # 指定颜色
      scale <- scale_color_manual(values = colors, na.value = na.value)
    # cols 长度>1，则手动颜色。
    } else {
      scale <- scale_color_manual(values = cols, na.value = na.value)
    }
    plot <- plot + scale
  }

  #(A17) 使用主题
  plot <- plot + theme_cowplot()
  return(plot)
}










() DefaultDimReduc
$ find . | grep "R$" | xargs grep -in "DefaultDimReduc" 2>/dev/null --color=auto
./seurat-object-4.0.4/R/utils.R:204:DefaultDimReduc <- function(object, assay = NULL) {

返回字符串，如 "umap"，默认的降维方法。

#' Find the default \code{\link{DimReduc}}
#'
#' Searches for \code{\link{DimReduc}s} matching \dQuote{umap}, \dQuote{tsne},
#' or \dQuote{pca}, case-insensitive, and in that order. Priority given to
#' \code{\link{DimReduc}s} matching the \code{DefaultAssay} or assay specified
#' (eg. \dQuote{pca} for the default assay weights higher than \dQuote{umap}
#' for a non-default assay)
#'
#' @param object A \code{\link{Seurat}} object
#' @param assay Name of assay to use; defaults to the default assay of the object
#'
#' @return The default \code{\link{DimReduc}}, if possible
#'
#' @export
#'
#' @examples
#' DefaultDimReduc(pbmc_small)
#'
DefaultDimReduc <- function(object, assay = NULL) {
  #(A1) 重建对象
  object <- UpdateSlots(object = object)
  #(A2) 如果实验为空，使用默认值
  assay <- assay %||% DefaultAssay(object = object)
  #(A3) 可用范围，共三个
  drs.use <- c('umap', 'tsne', 'pca')
  #(A4) 返回字符串，降维类中的对象名
  # FilterObjects() 见 详解 3-3.2
  # > FilterObjects(pbmc, 'DimReduc')
  # [1] "pca"  "umap" "tsne"
  dim.reducs <- FilterObjects(object = object, classes.keep = 'DimReduc')
  #(A5) 只保留使用当前 assay 的降维类
  # Filter() 高阶函数，见详解 9-3.1
  drs.assay <- Filter(
    f = function(x) {
      return(DefaultAssay(object = object[[x]]) == assay)
    },
    x = dim.reducs
  )

  #(A6) 如果 使用该assay的降维对象名长度大于0
  if (length(x = drs.assay) > 0) {
  	#(B1) 找出使用3个降维方法，在默认实验的降维类的名字向量中的下标，没找到返回0
    index <- lapply(
      X = drs.use, #遍历3元素向量
      FUN = grep,
      x = drs.assay, #c('umap', 'tsne', 'pca')
      ignore.case = TRUE
    )

    #(B2) 过滤掉长度为0的向量
    index <- Filter(f = length, x = index)
    # 如果长度>0
    if (length(x = index) > 0) {
      # 则返回 c('umap', 'tsne', 'pca') 中第一个匹配到的实验降维对象
      return(drs.assay[min(index[[1]])])
    }
  }
  # 默认参数则A6就返回了。

  #(A7) 如果降维对象长度不大于0，
  index <- lapply(
    X = drs.use, #遍历3元素向量
    FUN = grep,
    x = dim.reducs, #这个也不限制是不是当前asssay的降维了
    ignore.case = TRUE
  )
  #(A8) 过滤掉list中的0长元素
  index <- Filter(f = length, x = index)

  #(A9) 如果长度<1，那就是0了。
  if (length(x = index) < 1) {
  	# 报错: 
    stop(
      "Unable to find a DimReduc matching one of '",
      paste(drs.use[1:(length(x = drs.use) - 1)], collapse = "', '"),
      "', or '",
      drs.use[length(x = drs.use)],
      "', please specify a dimensional reduction to use",
      call. = FALSE
    )
  }
  return(dim.reducs[min(index[[1]])])
}










() LabelClusters() //todo

该函数对 ggplot2 拆解的很厉害，我感觉跟不上作者的思路了。
可能的补救途径：增加对 ggplot2 的理解，甚至有必要抽空再看看 ggplot2 的源码。 


$ find . | grep "R$" | xargs grep -in "LabelClusters" 2>/dev/null --color=auto
./seurat-4.1.0/R/visualization.R:4730:LabelClusters <- function(


#' Label clusters on a ggplot2-based scatter plot
#'
#' @param plot A ggplot2-based scatter plot
#' @param id Name of variable used for coloring scatter plot
#' @param clusters Vector of cluster ids to label
#' @param labels Custom labels for the clusters
#' @param split.by Split labels by some grouping label, useful when using
#' \code{\link[ggplot2]{facet_wrap}} or \code{\link[ggplot2]{facet_grid}}
#' @param repel Use \code{geom_text_repel} to create nicely-repelled labels
#' @param geom Name of geom to get X/Y aesthetic names for
#' @param box Use geom_label/geom_label_repel (includes a box around the text
#' labels)
#' @param position How to place the label if repel = FALSE. If "median", place
#' the label at the median position. If "nearest" place the label at the
#' position of the nearest data point to the median.
#' @param ... Extra parameters to \code{\link[ggrepel]{geom_text_repel}}, such as \code{size}
#'
#' @return A ggplot2-based scatter plot with cluster labels
#'
#' @importFrom stats median na.omit
#' @importFrom ggrepel geom_text_repel geom_label_repel
#' @importFrom ggplot2 aes_string geom_text geom_label layer_scales
#' @importFrom RANN nn2
#'
#' @export
#' @concept visualization
#'
#' @seealso \code{\link[ggrepel]{geom_text_repel}} \code{\link[ggplot2]{geom_text}}
#'
#' @examples
#' data("pbmc_small")
#' plot <- DimPlot(object = pbmc_small)
#' LabelClusters(plot = plot, id = 'ident')
#'
LabelClusters <- function(
  plot,
  id,  #用于颜色的变量名字
  clusters = NULL,
  labels = NULL,
  split.by = NULL,
  repel = TRUE,
  box = FALSE,
  geom = 'GeomPoint',
  position = "median",
  ...
) {
  #(A1) 获取ggplot2图形的坐标名字， GetXYAesthetics 见 详解 15-2.10
  # > g1$mapping$x
	#<quosure>
	#expr: ^mpg
	#env:  global
	#> as_label(g1$mapping$x)
	#[1] "mpg" 
  # > xynames <- unlist(x = Seurat:::GetXYAesthetics(plot = g1, geom = 'GeomPoint'), use.names = TRUE)
  #> xynames
  #    x      y
  # "mpg" "disp" 
  xynames <- unlist(x = GetXYAesthetics(plot = plot, geom = geom), use.names = TRUE)
  #(A2) 如果 染色的id不在 plot$data 列名中，则报错
  if (!id %in% colnames(x = plot$data)) {
    stop("Cannot find variable ", id, " in plotting data")
  }
  
  #(A3) 如果 split.by 非空，且 不在数据列名中
  if (!is.null(x = split.by) && !split.by %in% colnames(x = plot$data)) {
  	# 警告: 报错
  	# 小bug: 这里不是 id，是 split.by，一般也执行不到这一行
    warning("Cannot find splitting variable ", id, " in plotting data")
    split.by <- NULL
  }
  
  #(A4) 取子集，xy轴，颜色，分面。
  data <- plot$data[, c(xynames, id, split.by)]
  
  #(A5) id列的uniq值
  possible.clusters <- as.character(x = na.omit(object = unique(x = data[, id])))
  #(A6) 如果没有指定 clusters，则使用 id 列的uniq值
  groups <- clusters %||% as.character(x = na.omit(object = unique(x = data[, id])))

  #(A7) 如果 groups 有元素不在 possible.clusters 中，则报错
  if (any(!groups %in% possible.clusters)) {
    stop("The following clusters were not found: ", paste(groups[!groups %in% possible.clusters], collapse = ","))
  }
  #(A8) ggplot2 的渲染函数，可能需要读 ggplot2 //todo 以后再看
  pb <- ggplot_build(plot = plot)

  #(A9) layer_scales 是空间转录组吗？ //todo
  # > class(Seurat:::GeomSpatial)
  # [1] "GeomSpatial" "Geom"        "ggproto"     "gg"
  if (geom == 'GeomSpatial') {
  	# x 轴的范围 [1] 10.4 33.9
    xrange.save <- layer_scales(plot = plot)$x$range$range
    # y 轴的范围 [1]  71.1 472.0
    yrange.save <- layer_scales(plot = plot)$y$range$range
    # 这个对y轴的变换就很奇怪: 最大值 + 最小值 - 每个值，不理解为什么这么执行 // todo
    data[, xynames["y"]] = max(data[, xynames["y"]]) - data[, xynames["y"]] + min(data[, xynames["y"]])
    # 如果非空
    # > b$plot$plot_env
	# <environment: R_GlobalEnv>
    if (!pb$plot$plot_env$crop) {
      y.transform <- c(0, nrow(x = pb$plot$plot_env$image)) - pb$layout$panel_params[[1]]$y.range
      data[, xynames["y"]] <- data[, xynames["y"]] + sum(y.transform)
    }
  }
  #(A10) 添加color列，获取一个颜色字符串 ??
  # > head(b$data[[1]])
  #   colour    x   y PANEL group shape size fill alpha stroke
  #1 #00BA38 21.0 160     1     2    19  1.5   NA    NA    0.5
  #2 #00BA38 21.0 160     1     2    19  1.5   NA    NA    0.5
  # > b$data[[1]][[1]]
  # [1] "#00BA38"
  data <- cbind(data, color = pb$data[[1]][[1]])

  #(A11) 遍历 groups，求文字标签的坐标位置 
  labels.loc <- lapply(
    X = groups,
    FUN = function(group) { #对于每个 group 值
      #(B1) id 列是否等于 group，保持df结构
      data.use <- data[data[, id] == group, , drop = FALSE]
      #(B2) 如果 split.by 非空，
      data.medians <- if (!is.null(x = split.by)) {
      	# 则这个结构十分复杂，至于为什么需要转置t()？ 可能要反复调试才知道。
      	# do.call(rbind, lapply(X, function(x){  apply(...) }))
        do.call(
          what = 'rbind', #则调用 rbind 函数，
          # 合并 lapply() 函数返回的list
          args = lapply(
            X = unique(x = data.use[, split.by]), #遍历split.by 列的uniq值

            FUN = function(split) { #对于每个值: split
              medians <- apply(
              	# 获取 data.use 的子集： split.by 列的值为 split 的部分
              	# 按列求中位数
                X = data.use[data.use[, split.by] == split, xynames, drop = FALSE],
                MARGIN = 2,
                FUN = median,
                na.rm = TRUE
              )
              # 转置，强转为df
              medians <- as.data.frame(x = t(x = medians))
              # 新增列 split.by = split
              medians[, split.by] <- split
              return(medians)
            }

          )
        )
      #(B3) 如果 split.by 为空
      } else {
        as.data.frame(x = t(x = apply(
          X = data.use[, xynames, drop = FALSE], #对x、y坐标列，求中位数。
          MARGIN = 2,
          FUN = median,
          na.rm = TRUE
        )))
      }
      # 添加新列，分组列
      data.medians[, id] <- group
      # 使用一个颜色: 第一个颜色
      data.medians$color <- data.use$color[1]
      return(data.medians)
    }
  )

  #(A12) 如果位置是 "nearest"
  #感觉这里是为了避免标注点的 median 和谁都不接近，但是如果点足够多，就没这个担心了。
  if (position == "nearest") {
  	# 遍历 labels.loc 这个list
  	# 每一列是一个向量: x, y, 分组id, color
    labels.loc <- lapply(X = labels.loc, FUN = function(x) {
      # 数据中的分组列等于该标签的分组的，就是该分组的数据
      group.data <- data[as.character(x = data[, id]) == as.character(x[3]), ]
      # 获取最近的一个点的下标
      nearest.point <- nn2(data = group.data[, 1:2], query = as.matrix(x = x[c(1,2)]), k = 1)$nn.idx
      # 坐标更新
      x[1:2] <- group.data[nearest.point, 1:2]
      return(x)
    })
  }

  #(A13) list to df，见 3.14
  labels.loc <- do.call(what = 'rbind', args = labels.loc)
  #(A14) id列强转为因子
  labels.loc[, id] <- factor(x = labels.loc[, id], levels = levels(data[, id]))
  #(A15) 如果labels 为空，则使用 groups
  labels <- labels %||% groups
  #(A16) 如果 id 列的uniq值和 labels 不等，则报错
  if (length(x = unique(x = labels.loc[, id])) != length(x = labels)) {
    stop("Length of labels (", length(x = labels),  ") must be equal to the number of clusters being labeled (", length(x = labels.loc), ").")
  }
  #(A17) named vector
  names(x = labels) <- groups
  
  #(A18) 遍历 groups
  for (group in groups) {
  	# id列如果==group，则该列替换为 labels的值。
    labels.loc[labels.loc[, id] == group, id] <- labels[group]
  }

  #(A19) 如果需要盒子
  if (box) {
  	# 根据条件，挑选函数
    geom.use <- ifelse(test = repel, yes = geom_label_repel, no = geom_label)
    plot <- plot + geom.use(
      data = labels.loc,
      mapping = aes_string(x = xynames['x'], y = xynames['y'], label = id, fill = id),
      show.legend = FALSE,
      ...
    ) + scale_fill_manual(values = labels.loc$color[order(labels.loc[, id])])
  #A(20) 如果不需要盒子
  } else {
  	# geom_text，文字无背景
    geom.use <- ifelse(test = repel, yes = geom_text_repel, no = geom_text)
    # 不要fill参数
    plot <- plot + geom.use(
      data = labels.loc,
      mapping = aes_string(x = xynames['x'], y = xynames['y'], label = id),
      show.legend = FALSE,
      ...
    )
  }

  #(A20) 空间尺度，则限定显示范围
  # restore old axis ranges
  if (geom == 'GeomSpatial') {
    plot <- suppressMessages(expr = plot + coord_fixed(xlim = xrange.save, ylim = yrange.save))
  }
  
  return(plot)
}










() 主题函数 FacetTheme()/CenterTitle()
$ find . | grep "R$" | xargs grep -in "FacetTheme" 2>/dev/null --color=auto
./seurat-4.1.0/R/visualization.R:5733:FacetTheme <- function(...) {
见本文 3.16


$ find . | grep "R$" | xargs grep -in "CenterTitle" 2>/dev/null --color=auto
./seurat-4.1.0/R/visualization.R:5004:CenterTitle <- function(...) {
